package datatableaux.rules;

import java.util.Collections;
import java.util.HashSet;

import preprocessor.absorption.Absorption;

import kb.TBox;
import datatableaux.*;
import entities.Individual;
import entities.concept.*;

public class DataUnfoldingRule {

	DataCompletion dc;
	DataBranching br;
	TBox tb;
	boolean TuAxiom = false, TnuAxiom = false, TbAxiom = false;
	HashSet<Concept> unexpanded;
	
	public DataUnfoldingRule(DataCompletion dc) {
		super();
		this.dc = dc;
		this.br = this.dc.getCurrentBranching();
		this.tb = dc.getInitTBox();
	}
	
	public boolean isApplicable(Individual n){
		boolean app = false;
		HashSet<Concept> unexp = new HashSet<Concept> ();
		unexp.addAll(n.getLabels());
		unexp.removeAll(n.getUnfolededLabels());
		if(unexp.size()<1){
			return false;
		}
		unexpanded = unexp;
		//check if any other axioms to be unfolded
		if(Absorption.CA && !Collections.disjoint(tb.getTu().keySet(), unexp)){
			this.TuAxiom = true;
			return true;
		}
		if(Absorption.NCA && !Collections.disjoint(tb.getTnu().keySet(), unexp)){
			this.TnuAxiom = true;
			return true;
		}
		//binary axioms
		if(Absorption.BA){
			for(HashSet<PConcept> s:tb.getTb().keySet()){
			if(n.getLabels().containsAll(s) &&
					!n.getBinaryExpanded().contains(s)){
				//ensure at least one binary concept is not expanded.
				//otherwise no need to explore again
				this.TbAxiom = true;
				return true;
				}
			}
		}		
		return app;
	}
	
	public boolean applyRule(Individual ind){
		boolean suc = false;
		HashSet<Concept> newLabels = new HashSet<Concept>();
		if(this.TuAxiom){
			//apply unary axioms
			for(Concept c:this.unexpanded){
				if(c instanceof PConcept &&
						this.tb.getTu().containsKey(c)){
					Concept addc =  tb.getTu().get(c);
					ind.addUnfoldedLabels(c);
					if(DataBranchChanges.getRecordingStatus()){
						br.getBranchChanges().addToUnfoldedLabels(ind, c);
					}
//					for(Concept myc:ind.getLabels()){
//						System.out.println("ind "+ind +" already has label : "+myc);
//					}
//					System.out.println("to add: "+addc);
					if(ind.getLabels().contains(addc)){
						continue;
					}
					newLabels.add(addc);					
				}
			}
		}
		if(this.TnuAxiom){
			//apply negated unary axioms
			for(Concept c:this.unexpanded){
				if(c instanceof NotConcept &&
						this.tb.getTnu().containsKey(c)){
					Concept addc = tb.getTnu().get(c);
					ind.addUnfoldedLabels(c);
					if(DataBranchChanges.getRecordingStatus()){
						br.getBranchChanges().addToUnfoldedLabels(ind, c);
					}
					if(ind.getLabels().contains(addc)){
						continue;
					}
					newLabels.add(addc);				
				}
			}
		}
		if(this.TbAxiom){
			//apply binary axioms, note that the expanded concepts are not added
			//to expandedLabels...instead they are stored in the node's binaryExpanded
			//they will be added at the end of all rule applications!!!
			for(HashSet<PConcept> s:tb.getTb().keySet()){
				if(ind.getLabels().containsAll(s)){
					Concept addc = tb.getTb().get(s);
					if(!ind.getLabels().contains(addc)){
						newLabels.add(addc);
						HashSet<Concept> ns = new HashSet<Concept>();
						ns.addAll(s);
						ind.addBinaryExpanded(ns);
						if(DataBranchChanges.getRecordingStatus()){
							for(Concept c:ns){
								br.getBranchChanges().addToUnfoldedLabels(ind, c);
							}
							
						}
					}					
				}
			}
		}
		//add changes
		if(DataBranchChanges.getRecordingStatus()){
			br.getBranchChanges().addToLabel(ind, newLabels);
		}
		ind.addLabels(newLabels);
		suc = !newLabels.isEmpty();
		return suc;
	}
	
	
	
	
}
